package com.starview.star.operators.arrays.runtime;

import java.util.ArrayList;
import java.util.HashSet;

import com.starview.platform.data.EvaluationException;
import com.starview.platform.data.IConstructor;
import com.starview.platform.data.IFunction;
import com.starview.platform.data.IValue;
import com.starview.platform.data.type.IType;
import com.starview.platform.data.type.Refresher;
import com.starview.platform.data.type.StandardTypes;
import com.starview.platform.data.type.TypeUtils;
import com.starview.platform.data.type.TypeVar;
import com.starview.platform.data.value.Array;
import com.starview.platform.data.value.Factory;
import com.starview.star.operators.CafeEnter;

public class ArrayQuerySupport
{
  public static class ArrayProject0 implements IFunction
  {
    public static final String name = "__array_project_0";

    @CafeEnter
    public static Array enter(Array els) throws EvaluationException
    {
      IValue projected[] = new IValue[els.size()];
      for (int ix = 0; ix < els.size(); ix++) {
        IValue el = els.getCell(ix);
        if (!(el instanceof IConstructor))
          throw new EvaluationException("illegal entry");
        else
          projected[ix] = ((IConstructor) el).getCell(0);
      }

      return Array.newArray(projected);
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter((Array) args[0]);
    }

    @Override
    public IType getType()
    {
      return type();
    }

    public static IType type()
    {
      IType v1 = new TypeVar();
      IType v2 = new TypeVar();
      return Refresher.generalize(TypeUtils.functionType(TypeUtils.arrayType(TypeUtils.tupleType(v1, v2)), TypeUtils
          .arrayType(v1)));
    }
  }

  public static class ArrayUnique implements IFunction
  {
    public static final String name = "__array_unique";

    @CafeEnter
    public static Array enter(Array data, IFunction equalizer) throws EvaluationException
    {
      HashSet<Integer> seenHashes = new HashSet<Integer>();
      ArrayList<IValue> reslt = new ArrayList<IValue>(data.size());

      // only assume that two things that hash to different things are different
      for (IValue el : data) {
        if (seenHashes.contains(el.hashCode())) {
          boolean found = false;
          for (IValue el2 : reslt) {
            if (equalizer.enter(el, el2).equals(Factory.trueValue)) {
              found = true;
              break;
            }
          }
          if (!found) {
            reslt.add(el);
          }
        } else {
          reslt.add(el);
          seenHashes.add(el.hashCode());
        }
      }
      return Array.newArray(reslt);
    }

    @Override
    public IValue enter(IValue... args) throws EvaluationException
    {
      return enter((Array) args[0], (IFunction) args[1]);
    }

    @Override
    public IType getType()
    {
      return type();
    }

    public static IType type()
    {
      IType v = new TypeVar();
      IType equalityType = TypeUtils.functionType(v, v, StandardTypes.booleanType);
      IType listType = TypeUtils.arrayType(v);
      return Refresher.generalize(TypeUtils.functionType(listType, equalityType, listType));
    }
  }
}
